
/**
\file Definition of nmn::math::square_matrix.
\author Sean Tromans
*/

#ifndef	NMN_TEMPLATES_MATH_MATRIX_SQUARE_HPP
#define	NMN_TEMPLATES_MATH_MATRIX_SQUARE_HPP

#include <needs_more_ninja/template/math/vector/vector.hpp>

#ifndef	NMN_MATH_SQUARE_MATRIX_MAX_ARITY
#define	NMN_MATH_SQUARE_MATRIX_MAX_ARITY NMN_MATH_VECTOR_MAX_ARITY
#endif

#define NMN_MATH_MATRIX_CONSTRUCTOR( z, n, _ )	\
	template < BOOST_PP_ENUM_PARAMS_Z(z, BOOST_PP_INC(n), typename TVectorType) >	\
	square_matrix( BOOST_PP_ENUM_BINARY_PARAMS_Z(z, BOOST_PP_INC(n), TVectorType, const & p) );

namespace nmn
{

	namespace math
	{

		/**
		Generic statically sized square matrix type.
		\author Sean Tromans
		*/
		template < typename TValueType, size_t Size >
		class square_matrix	:	public	boost::multipliable1< typename square_matrix< TValueType, Size >,
										boost::multiplicative2< typename square_matrix< TValueType, Size >, TValueType,
										boost::equality_comparable1< typename square_matrix< TValueType, Size > > > >
		{
			//
			//	Public Member Types
			//
			public:

				typedef	square_matrix< TValueType, Size >		my_type;
				typedef nmn::math::vector< TValueType, Size >	vector_type;
				typedef boost::array< vector_type, Size >		array_type;
				typedef std::size_t								size_type;
				enum	{	static_size = Size	};

				typedef	vector_type								value_type;
				typedef	value_type &							reference_type;
				typedef	value_type const &						const_reference_type;

				typedef TValueType								scalar_type;

				typedef typename array_type::iterator				iterator;
				typedef typename array_type::const_iterator			const_iterator;
				typedef typename array_type::reverse_iterator		reverse_iterator;
				typedef typename array_type::const_reverse_iterator	const_reverse_iterator;


			//
			//	Public Member Functions
			//
			public:

				/**
				Default Constructor. Does nothing.
				\author Sean Tromans
				*/
				square_matrix( );

				/**
				Copy construct from another matrix of greater than or equal size, but potentially a different type.
				@param rhs The matrix to copy.
				\author Sean Tromans
				*/
				template < typename TOtherValueType >
				square_matrix( square_matrix< TOtherValueType, Size > const & rhs );

				/**
				Construct a matrix from an array of vectors.
				@param values The array of vectors to copy.
				*/
				template < typename TOtherValueType >
				square_matrix( boost::array< nmn::math::vector< TOtherValueType, Size >, Size > const & values );

				BOOST_PP_REPEAT_FROM_TO( 1, NMN_MATH_SQUARE_MATRIX_MAX_ARITY, NMN_MATH_MATRIX_CONSTRUCTOR, 0 );

				/**
				Assign this matrix to be equal to another.
				@param rhs The matrix to copy.
				\author Sean Tromans
				*/
				template < typename TOtherValueType >
				my_type & operator = ( square_matrix< TOtherValueType, Size > const & rhs );

				/**
				The size of the matrix.
				\author Sean Tromans
				*/
				size_type size( ) const;

				/**
				Component wise multiplication of another matrix onto this.
				@param rhs The matrix to be multiplied.
				@return A reference to this after the multiplication.
				\author Sean Tromans
				*/
				my_type & operator *= ( my_type const & rhs );

				/**
				Component wise multiplication of a value type onto this.
				@param rhs The value type to be multiplied.
				@return A reference to this after the multiplication.
				\author Sean Tromans
				*/
				my_type & operator *= ( scalar_type const & rhs );

				/**
				Component wise strict equality check against another matrix.
				@param rhs The matrix to compare against.
				@return True if each corresponding component in the two matrices are exactly equal.
				\author Sean Tromans
				*/
				bool strict_equals( my_type const & rhs ) const;

				/**
				Gets a column from the matrix.
				@param index The index of the column to return.
				@return a reference to the column at \c index.
				\author Sean Tromans
				*/
				reference_type operator[] ( size_type index );

				/**
				Gets a column from the matrix.
				@param index The index of the column to return.
				@return a constant reference to the column at \c index.
				\author Sean Tromans
				*/
				const_reference_type operator[] ( size_type index ) const;

				/**
				Gets a column from the matrix.
				@param index The index of the column to return.
				@return a reference to the column at \c index.
				\author Sean Tromans
				*/
				reference_type at( size_type index );

				/**
				Gets a column from the matrix.
				@param index The index of the column to return.
				@return a constant reference to the column at \c index.
				\author Sean Tromans
				*/
				const_reference_type at( size_type index ) const;

				/**
				Returns an iterator to the first column in the matrix.
				\author Sean Tromans
				*/
				iterator begin( );

				/**
				Returns a delimiting iterator to the matrix.
				\author Sean Tromans
				*/
				iterator end( );

				/**
				Returns a constant iterator to the first column in the matrix.
				\author Sean Tromans
				*/
				const_iterator begin( ) const;

				/**
				Returns a delimiting constant iterator to the matrix.
				\author Sean Tromans
				*/
				const_iterator end( ) const;

				/**
				Returns a constant iterator to the first element in the matrix.
				\author Sean Tromans
				*/
				const_iterator cbegin( ) const;

				/**
				Returns a delimiting constant iterator to the matrix.
				\author Sean Tromans
				*/
				const_iterator cend( ) const;

				/**
				Returns a reverse iterator to the last column in the matrix.
				\author Sean Tromans
				*/
				reverse_iterator rbegin( );

				/**
				Returns a delimiting reverse iterator to the matrix.
				\author Sean Tromans
				*/
				reverse_iterator rend( );

				/**
				Returns a constant reverse iterator to the last column in the matrix.
				\author Sean Tromans
				*/
				const_reverse_iterator rbegin( ) const;

				/**
				Returns a delimiting constant reverse iterator to the matrix.
				\author Sean Tromans
				*/
				const_reverse_iterator rend( ) const;

				/**
				Swaps this matrix with another.
				@param rhs The matrix to swap with.
				\author Sean Tromans
				*/
				void swap( my_type & rhs );

			//
			// Private Member Variables
			//
			private:

				array_type m_array;

				static_assert( static_size >= 2, "matrix must have at least two components." );
				static_assert( boost::is_arithmetic< TValueType >::value, "TValueType must be a numeric value type." );
		};

		/**
		\relates nmn::math::square_matrix
		Outputs a square_matrix to an output stream.
		@param os The output ostream.
		@param vec The square_matrix to output.
		@return A reference to the output stream.
		\author Sean Tromans
		*/
		template < typename Elem, typename Traits, typename TValueType, size_t Size >
		inline std::basic_ostream< Elem, Traits > & operator << ( std::basic_ostream< Elem, Traits > & os, square_matrix< TValueType, Size > const & vec );

		/**
		\relates nmn::math::square_matrix
		Component wise absolute equality check.
		@param lhs The first square_matrix to compare.
		@param rhs The second square_matrix to compare.
		@return true if the two square_matrixs are exactly equal.
		\author Sean Tromans
		*/
		template < typename TValueType, size_t Size >
		bool operator == ( square_matrix< TValueType, Size > const & lhs, square_matrix< TValueType, Size > const & rhs );

		/**
		\relates nmn::math::square_matrix
		Linear algebra multiplication of a vector with a matrix.
		@param lhs The matrix to be multiplied.
		@param rhs The vector to be multiplied.
		@return A reference to this after the multiplication.
		\author Sean Tromans
		*/
		template< typename TValueType, typename TOtherValueType, std::size_t Size >
		inline vector< TOtherValueType, Size >
			operator * (	square_matrix< TValueType, Size > lhs,
							vector< TOtherValueType, Size > const & rhs );

	}

}

#undef NMN_MATH_MATRIX_CONSTRUCTOR

#endif	//NMN_TEMPLATES_MATH_MATRIX_SQUARE_HPP